package state.socket;

import java.util.Vector;

import state.util.GameStringUtils;
import state.util.Hashtable;
import state.util.SerialUtil;
import state.util.GameFloat;
import state.util.array.ByteArray;

public class MapDataPackage {
  public static final int BYTE_ARRAY_TYPE = 0;
  public static final int INT_TYPE = 1;
  public static final int DOUBLE_TYPE = 2;
  public static final int LONG_TYPE = 3;
  public static final int STRING_TYPE = 4;

  private DataPackage innerDataPackage = null;

  private Hashtable dataByName = new Hashtable();
  private Hashtable typeByName = new Hashtable();

  private static final String[] DATA_TYPE_NAME = new String[]{"Byte Array", "integer", "double", "long", "String"};

  private int size = 0;

  public static MapDataPackage getMapPackage(DataPackage dataPackage) {
    return new MapDataPackage(dataPackage);
  }

  public static MapDataPackage getMapPackage1(int header) {
    return new MapDataPackage(DataPackage.createDataPackage1(header));
  }

  private MapDataPackage(DataPackage dataPackage) {
    innerDataPackage = dataPackage;
    while (innerDataPackage.hasMoreData1(4)) {
      String name = innerDataPackage.nextString();
      int type = innerDataPackage.nextInt();
      typeByName.put(name, new Integer(type));

      switch (type) {
      case BYTE_ARRAY_TYPE:
        dataByName.put(name, innerDataPackage.nextByteArray());
        break;
      case INT_TYPE:
        dataByName.put(name, new Integer(innerDataPackage.nextInt()));
        break;
      case DOUBLE_TYPE:
        dataByName.put(name, new Double(innerDataPackage.nextDouble()));
        break;
      case LONG_TYPE:
        dataByName.put(name, new Long(innerDataPackage.nextLong()));
        break;
      case STRING_TYPE:
        dataByName.put(name, innerDataPackage.nextString());
        break;
      default:
        System.out.println("MapDataPackage : MapDataPackage : Can not relize the data type: " + type);
      }
      size++;
    }
    innerDataPackage.expandSize();
  }

  public Vector getNames() {
    return typeByName.keys();
  }

  public int getHeader() {
    return innerDataPackage.getHeader();
  }

  public void setHeader(int header) {
    innerDataPackage.setHeader(header);
  }

  public int getDataLength() {
    return innerDataPackage.getDataLength();
  }

  public DataPackage getOriginDataPackage() {
    return innerDataPackage;
  }
  
  public ByteArray getDataAt(int index) {
    if (index > dataByName.size() - 1) {
      System.out.println("MapDataPackage : getDataAt : wrong index: " + index + "/" + dataByName.size());
      return ByteArray.create(0);
    }
    
    Vector keys = dataByName.keys();
    String name = String.valueOf(keys.elementAt(index));
    Integer type = (Integer) typeByName.get(name);
    ByteArray results = null;
    switch (type.intValue()) {
    case BYTE_ARRAY_TYPE:
      results = (ByteArray) dataByName.get(name);
      break;
    case INT_TYPE:
      results = SerialUtil.serialNumberFromInt(((Integer) dataByName.get(name)).intValue());
      break;
    case DOUBLE_TYPE:
      results = doubleToByteArray(((GameFloat) dataByName.get(name)).floatValue());
      break;
    case LONG_TYPE:
      results = SerialUtil.serialNumberFromLong(((Integer) dataByName.get(name)).intValue());
      break;
    case STRING_TYPE:
      results = GameStringUtils.toByteArrayUTF8(String.valueOf(dataByName.get(name)));
      break;
    default:
      System.out.println("MapDataPackage : getDataAt : Can not relize the data type: " + type);
    }
    return results;
  }
  
  private ByteArray doubleToByteArray(float value) {
    // Lấy vị trí của dấu .
    String str = String.valueOf(value);
    int tmp = str.indexOf('.');
    if (tmp == -1) {
      tmp = str.length();
    }

    // Lấy độ dài số chữ số sau dấu phẩy
    int tmp1 = str.length() - tmp - 1;
    if (tmp1 == -1) {
      tmp1 = 0;
    }

    // Đưa số double lên thành kiểu long
    int tmp3 = 1;
    for (int i = 0; i < tmp1; i++) {
      tmp3 *= 10;
    }
    int tmp2 = (int) (value * tmp3);
    
    ByteArray results = ByteArray.create(12);
    ByteArray.arraycopy(SerialUtil.serialNumberFromLong(tmp2), 0, results, 0, 8);
    ByteArray.arraycopy(SerialUtil.serialNumberFromInt(tmp1), 0, results, 8, 4);
    return results;
  }
  
  public DataPackage getUpdatedDataPackage() {
    DataPackage dataPackage = DataPackage.createDataPackage1(innerDataPackage.getHeader());
    Vector keys = dataByName.keys();
    for (int i = 0; i < keys.size(); i++) {
      String name = String.valueOf(keys.elementAt(i));
      Integer type = (Integer) typeByName.get(name);
      
      dataPackage.putString(name);
      dataPackage.putInt(type.intValue());
      switch (type.intValue()) {
      case BYTE_ARRAY_TYPE:
        dataPackage.putByteArray((ByteArray) dataByName.get(name));
        break;
      case INT_TYPE:
        dataPackage.putInt(((Integer) dataByName.get(name)).intValue());
        break;
      case DOUBLE_TYPE:
        dataPackage.putDouble(((GameFloat) dataByName.get(name)).floatValue());
        break;
      case LONG_TYPE:
        dataPackage.putLong(((Integer) dataByName.get(name)).intValue());
        break;
      case STRING_TYPE:
        dataPackage.putString(String.valueOf(dataByName.get(name)));
        break;
      default:
        System.out.println("MapDataPackage : getUpdatedDataPackage : Can not relize the data type: " + type);
      }
    }
    innerDataPackage = dataPackage;
    return dataPackage;
  }

  public int getSize() {
    return size;
  }

  public void putByteArray(String name, ByteArray bytes) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(BYTE_ARRAY_TYPE);
    innerDataPackage.putByteArray(bytes);
    dataByName.put(name, bytes);
    typeByName.put(name, new Integer(BYTE_ARRAY_TYPE));
    size++;
  }

  public void putInt(String name, int value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(INT_TYPE);
    innerDataPackage.putInt(value);
    dataByName.put(name, new Integer(value));
    typeByName.put(name, new Integer(INT_TYPE));
    size++;
  }

  public void putDouble(String name, float value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(DOUBLE_TYPE);
    innerDataPackage.putDouble(value);
    dataByName.put(name, new GameFloat(value));
    typeByName.put(name, new Integer(DOUBLE_TYPE));
    size++;
  }

  public void putLong(String name, int value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(LONG_TYPE);
    innerDataPackage.putLong(value);
    dataByName.put(name, new Long(value));
    typeByName.put(name, new Integer(LONG_TYPE));
    size++;
  }

  public void putString(String name, String value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(STRING_TYPE);
    innerDataPackage.putString(value);
    dataByName.put(name, value);
    typeByName.put(name, new Integer(STRING_TYPE));
    size++;
  }

  public ByteArray getByteArray(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      System.out.println("MapDataPackage : getByteArray : MapDataPackage is not contain data name: " + name);
      return null;
    }
    
    if (type.intValue() != BYTE_ARRAY_TYPE) {
      System.out.println("MapDataPackage : getByteArray : Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Byte Array");
      return null;
    }
    return (ByteArray) dataByName.get(name);
  }

  public int getInt(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      System.out.println("MapDataPackage : getInt : MapDataPackage is not contain data name: " + name);
      return 0;
    }
    
    if (type.intValue() != INT_TYPE) {
      System.out.println("MapDataPackage : getInt : Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Integer");
      return 0;
    }
    
    return ((Integer) dataByName.get(name)).intValue();
  }

  public float getDouble(String name) {
    Integer type = (Integer) typeByName.get(name);
    
    if (type == null) {
      System.out.println("MapDataPackage : getDouble : MapDataPackage is not contain data name: " + name);
      return 0;
    }
    
    if (type.intValue() != DOUBLE_TYPE) {
      System.out.println("MapDataPackage : getDouble : Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Double");
      return 0;
    }
    return ((GameFloat) dataByName.get(name)).floatValue();
  }

  public int getLong(String name) {
    Integer type = (Integer) typeByName.get(name);
    
    if (type == null) {
      System.out.println("MapDataPackage : getLong : MapDataPackage is not contain data name: " + name);
      return 0;
    }
    
    if (type.intValue() != LONG_TYPE) {
      System.out.println("MapDataPackage : getLong : Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Long");
      return 0;
    }
    return ((Integer) dataByName.get(name)).intValue();
  }

  public String getString(String name) {
    Object data = dataByName.get(name);
    
    if (data == null) {
      System.out.println("MapDataPackage : getString : MapDataPackage is not contain data name: " + name);
      return "";
    }
    return String.valueOf(data);
  }

  public ByteArray popByteArray(String name) {
    ByteArray value = getByteArray(name);
    removeData(name);
    return value;
  }

  public int popInt(String name) {
    int value = getInt(name);
    removeData(name);
    return value;
  }

  public float popDouble(String name) {
    float value = getDouble(name);
    removeData(name);
    return value;
  }

  public int popLong(String name) {
    int value = getLong(name);
    removeData(name);
    return value;
  }

  public String popString(String name) {
    String value = getString(name);
    removeData(name);
    return value;
  }

  public int getTypeOf(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      System.out.println("MapDataPackage : getTypeOf : MapDataPackage is not contain data name: " + name);
      return INT_TYPE;
    }
    return type.intValue();
  }

  public void removeData(String name) {
    typeByName.remove(name);
    dataByName.remove(name);
  }

  public Object get(String name) {
    Object data = dataByName.get(name);
    if (data == null) {
      System.out.println("MapDataPackage : get : MapDataPackage is not contain data name: " + name);
      return null;
    }
    return data;
  }

  public Object clone() {
    return new MapDataPackage((DataPackage) innerDataPackage.clone());
  }
  
  public void detroy() {
    innerDataPackage.detroy();
  }
}
